
#ifndef _ALITTLE_SCRIPTSYSTEM_H_
#define _ALITTLE_SCRIPTSYSTEM_H_

#include "LuaTinker.h"

#include <string>

namespace ALittle
{

class ScriptSystem
{
public:
	static ScriptSystem& Instance();

public:
	void Setup();
	void Shutdown();

public:
	void Reset();
	void Clear();

public:
	void RunScriptFile(const std::string& scriptFileName);
	void RunScript(const std::string& scriptContent);
	void GarbageCollection();

public:
	template <class T>
	class ClassRegister
	{
	public:
		ClassRegister(lua_State* L, const char* name) : m_L(L)
		{
			LuaTinker::class_add<T>(m_L, name);
		}

		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor0<T>);
			return *this;
		}
		
		template <class T1>
		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor1<T, T1>);
			return *this;
		}

		template <class T1, class T2>
		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor2<T, T1, T2>);
			return *this;
		}

		template <class T1, class T2, class T3>
		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor3<T, T1, T2, T3>);
			return *this;
		}

		template <class T1, class T2, class T3, class T4>
		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor4<T, T1, T2, T3, T4>);
			return *this;
		}

		template <class T1, class T2, class T3, class T4, class T5>
		ClassRegister& Constructor()
		{
			LuaTinker::class_con<T>(m_L, LuaTinker::constructor5<T, T1, T2, T3, T4, T5>);
			return *this;
		}

		template <class P>
		ClassRegister& Inherit()
		{
			LuaTinker::class_inh<T, P>(m_L);
			return *this;
		}

		template <typename VAR>
		ClassRegister& AddMemberVar(const char* name, VAR T::*val) 
		{
			LuaTinker::class_mem<T>(m_L, name, val);
			return *this;
		}

		template <class F>
		ClassRegister& AddMemberFunction(const char* name, F func)
		{
			LuaTinker::class_def<T>(m_L, name, func);
			return *this;
		}

		template <class F>
		ClassRegister& AddConstructor(F func)
		{
			LuaTinker::class_con<T>(m_L, func);
			return *this;
		}

	private:
		lua_State* m_L;
	};

public:
	template <class T>
	ClassRegister<T> RegisterClass2Script(const char* name) 
	{
		return ClassRegister<T>(m_L, name);
	}

public:
	template<class F>
	void AddFunction(const char* name, F func)
	{
		LuaTinker::def(m_L, name, func);
	}

public:
	template <class T>
	void SetValue(const char* name, T t)
	{
		LuaTinker::set(m_L, name, t);
	}

public:
	template<class R>
	R Invoke(const char* name)
	{
		return LuaTinker::call<R>(m_L, name);
	}

	template<class R, class Arg1>
	R Invoke(const char* name, const Arg1& a1)
	{
		return LuaTinker::call<R>(m_L, name, a1);
	}

	template<class R, class Arg1, class Arg2>
	R Invoke(const char* name, const Arg1& a1, const Arg2& a2)
	{
		return LuaTinker::call<R>(m_L, name, a1, a2);
	}

	template<class R, class Arg1, class Arg2, class Arg3>
	R Invoke(const char* name, const Arg1& a1, const Arg2& a2, const Arg3& a3)
	{
		return LuaTinker::call<R>(m_L, name, a1, a2, a3);
	}

	template<class R, class Arg1, class Arg2, class Arg3, class Arg4>
	R Invoke(const char* name, const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4)
	{
		return LuaTinker::call<R>(m_L, name, a1, a2, a3, a4);
	}

	template<class R, class Arg1, class Arg2, class Arg3, class Arg4, class Arg5>
	R Invoke(const char* name, const Arg1& a1, const Arg2& a2, const Arg3& a3, const Arg4& a4, const Arg5& a5)
	{
		return LuaTinker::call<R>(m_L, name, a1, a2, a3, a4, a5);
	}

private:	
	lua_State* m_L;

public:
	ScriptSystem();
	~ScriptSystem();
};

} // ALittle

#define g_ScriptSystem	ALittle::ScriptSystem::Instance()

#endif // _ALITTLE_SCRIPTSYSTEM_H_
